We didn’t “fix NAT.” We built a SIP.js diagnostic lab — and one-way audio started telling the truth.
Most VoIP war stories end the same way:
“It’s NAT.” “Change ext-rtp-ip.” “Turn on STUN.” “Restart FreeSWITCH.”
Sometimes that helps. Often it hides the real failure.
We had a classic case: browser extension ↔ FusionPBX/FreeSWITCH ↔ device (ESP32 SIP). Signaling looked fine. Calls connected. DTLS came up. And still — one-sided audio.
So instead of shipping another config change into production, we did something different.
We built a minimal SIP.js diagnostic lab.
Same SIP.js major line as the product client. Same audio constraints. Same FusionPBX / WSS endpoint. No Dashboard chrome. No DeepFilter. No “maybe the UI ate the track.”
Just:
Chrome → SIP.js → WSS → FreeSWITCH → endpoint
With live WebRTC stats on screen: ICE pair, inbound/outbound RTP, audioLevel, mic device, <audio> playout state.
That single decision changed the investigation.
What “working SIP” can still hide
In real captures we saw patterns teams mislabel as “NAT is broken”:
1. REGISTER / INVITE / 200 / ACK succeed — and audio still fails.
2. ICE + DTLS connected — packets arrive — inbound audioLevel sits near the noise floor.
3. ESP32 not registered — FreeSWITCH returns USER_NOT_REGISTERED / 480 — looks like “no audio,” is actually “no B-leg.”
4. Forked / stale contacts — one extension, multiple dead private IPs.
5. Wrong capture device — Chrome on “Stereo Mix” instead of a mic → almost no outbound PCMU.
6. Client playout bugs — Unified Plan ontrack with empty streams[], muted <audio> behind a noise-suppression pipeline, HTTPS not used so getUserMedia is blocked.
None of those are fixed by flipping Sofia NAT in a panic.
The lab forced an honest question:
Are we missing packets — or delivering silence?
Those are different products. Different owners. Different SLAs.
Why a SIP lab is a business asset (not just an engineer toy)
If you sell or operate anything in:
· CCaaS / contact center
· Voice dialers (preview, progressive, predictive)
· AMD (answering machine detection)
· Call recording + transcript + analytics
· Voicemail, fax, SMS, omnichannel
· Custom FusionPBX / FreeSWITCH platforms
…then one-way audio is not a “telecom curiosity.”
It is:
· abandoned carts and missed callbacks
· agent trust collapse
· compliance risk on recordings that contain no usable media
· false “carrier outage” tickets
· weeks of finger-pointing between app, device, and PBX teams
A diagnostic SIP client gives you a control plane for truth:
Question
Lab answer in minutes
Is WSS/TLS the problem?
Connect or fail before SIP
Is the extension registered?
Sofia / REGISTER visible
Did ICE/DTLS finish?
Live peer connection state
Are RTP packets flowing?
packetsReceived / packetsSent
Is the payload actually voice?
audioLevel / energy vs silence
Is the browser even playing?
<audio> srcObject, paused, muted
Is product UI the culprit?
Lab works, Dashboard doesn’t → application
That last row alone pays for the lab.
What we changed — and what we deliberately did not
Application / lab side
· Always attach remote media: streams[0] or MediaStream([event.track])
· Plain playout path (no silent element behind a filter pipeline)
· Live diagnostics for every call
· HTTPS secure context for microphone access
· Explicit handling of FreeSWITCH self-signed WSS on :7443 (port 443 cert ≠ WSS cert)
Server side
We did not treat Sofia ext-rtp-ip / auto-NAT / codec rewrites as the “fix” without packet evidence.
That discipline matters.
In VoIP consulting, the expensive mistake is shipping irreversible infra changes for a client bug that was:
· registration hygiene
· device mic/gain
· WebRTC attach logic
· or a wrong Chrome device
From one lab to a platform practice
Once you can isolate the voice path, the same engineering muscle builds real business systems:
Inbound / outbound voice Queues, IVR, skills-based routing, click-to-call, softphones, WebRTC agent desktops.
Dialers Preview, progressive, and predictive dialers with pacing, abandonment controls, and AMD — not just “fire INVITEs.”
AMD & call progress Machine vs human vs fax tone — with logging that QA and compliance can audit.
Recording stack Stereo/leg recording, retention policies, redaction, transcription, sentiment / talk-ratio / silence detection, dispute playback.
Messaging & fax SMS workflows beside voice, inbound fax to email/S3, voicemail-to-text.
FusionPBX / FreeSWITCH customization Multi-tenant domains, WebRTC profiles, device fleets (including SIP endpoints / ESP-class hardware), CRM / helpdesk / EHR connectors, billing hooks.
CCaaS-shaped products White-label agent UI, supervisor wallboards, campaign APIs, webhook events, multi-region media.
The SIP lab is the scalpel. The platform is the business.
Who this is for
If any of these sound familiar, we should talk:
· “Calls connect but customers hear nothing.”
· “Works on MicroSIP, fails in our React softphone.”
· “We need FusionPBX customized for our vertical.”
· “We’re building a dialer / AMD / recording analytics product and don’t want to learn FreeSWITCH the hard way.”
· “We need a consultant who can read RTP and ship the application.”
We help teams with:
· FusionPBX / FreeSWITCH architecture & hardening
· WebRTC / SIP.js softphones and agent desktops
· Custom CCaaS modules
· Voice dialers & AMD
· Recording, transcription, and call analytics pipelines
· Device ↔ cloud SIP bridges
· Root-cause labs for one-way / no-audio / intermittent media
Not slideware. Packet evidence. Then product.
Closing
One-way audio taught us a rule we now use on every engagement:
Don’t change the PBX until the client can prove whether the stream is missing — or silent.
A SIP.js diagnostic lab makes that proof cheap.
If you’re building or bleeding on voice, SMS, fax, voicemail, recording, transcripts, AMD, or dialers around FusionPBX / FreeSWITCH / WebRTC — comment LAB or DM me.
Happy to share the checklist we use before anyone touches NAT.
